package cc.shacocloud.mirage.utils;

import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;

import java.util.function.Function;
import java.util.regex.Pattern;

/**
 * 用于格式化和记录消息的实用工具方法
 */
@Slf4j
public abstract class LogFormatUtils {
    
    private static final Pattern NEWLINE_PATTERN = Pattern.compile("[\n\r]");
    
    private static final Pattern CONTROL_CHARACTER_PATTERN = Pattern.compile("\\p{Cc}");
    
    
    /**
     * {@link #formatValue(Object, int, boolean)} 的方便变体，它将日志消息的长度限制为 100 个字符，
     * 如果 {@code limitLength} 设置为“true”，还会替换换行符和控制字符。
     *
     * @param value       要设置格式的值
     * @param limitLength 是否截断长度为 100 的值
     * @return 格式化的值
     */
    public static String formatValue(@Nullable Object value, boolean limitLength) {
        return formatValue(value, (limitLength ? 100 : -1), limitLength);
    }
    
    /**
     * 通过 {@code toString } 格式化给定值，如果它是 {@link CharSequence}，则引用它，在指定的 {@code maxLength} 处截断，
     * 并在设置 {@code replaceNewLines} 时将其压缩为一行
     *
     * @param value                               要格式化的值
     * @param maxLength                           最大长度，之后要截断，或 -1 表示无限制
     * @param replaceNewlinesAndControlCharacters 是否将换行符和控制字符替换为占位符
     * @return 格式化的值
     */
    public static String formatValue(@Nullable Object value,
                                     int maxLength,
                                     boolean replaceNewlinesAndControlCharacters) {
        
        if (value == null) {
            return "";
        }
        String result;
        try {
            result = Utils.nullSafeToString(value);
        } catch (Throwable ex) {
            result = Utils.nullSafeToString(ex);
        }
        if (maxLength != -1) {
            result = (result.length() > maxLength ? result.substring(0, maxLength) + " (truncated)..." : result);
        }
        if (replaceNewlinesAndControlCharacters) {
            result = NEWLINE_PATTERN.matcher(result).replaceAll("<EOL>");
            result = CONTROL_CHARACTER_PATTERN.matcher(result).replaceAll("?");
        }
        if (value instanceof CharSequence) {
            result = "\"" + result + "\"";
        }
        return result;
    }
    
    /**
     * 使用此选项可在 TRACE 与 DEBUG 日志级别记录具有不同详细级别（或不同消息）的消息。
     * 实际上，可以替代：
     * <pre class="code">
     * if (logger.isDebugEnabled()) {
     *   String str = logger.isTraceEnabled() ? "..." : "...";
     *   if (logger.isTraceEnabled()) {
     *     logger.trace(str);
     *   }
     *   else {
     *     logger.debug(str);
     *   }
     * }
     * </pre>
     *
     * @param logger         用于记录消息的记录器
     * @param messageFactory 接受设置为 {@link Logger#isTraceEnabled} 值的布尔值的函数
     */
    public static void traceDebug(@NotNull Logger logger, Function<Boolean, String> messageFactory) {
        if (logger.isDebugEnabled()) {
            boolean traceEnabled = logger.isTraceEnabled();
            String logMessage = messageFactory.apply(traceEnabled);
            if (traceEnabled) {
                logger.trace(logMessage);
            } else {
                logger.debug(logMessage);
            }
        }
    }
}
